import { createResolvablePromise } from '@/utils/common'
import { LoadStatus } from '../../types'
import BaseLoadHandler from './BaseLoadHandler'

const cacheName = 'upscale-model-cache'

let db: IDBDatabase | null = {} as any
let awaitDbLoad: Promise<void> = {} as any
if (typeof window !== 'undefined') {
  const request = indexedDB.open(cacheName, 2)
  const [_awaitDbLoad, resolveAwaitDbLoad] = createResolvablePromise<void>()
  awaitDbLoad = _awaitDbLoad
  request.onsuccess = (e) => {
    db = (e.target as any).result
    resolveAwaitDbLoad()
  }
  request.onerror = () => console.error('indexedDB open error')
  request.onupgradeneeded = (e) => {
    const db = (e.target as any).result
    if (!db.objectStoreNames.contains(cacheName)) {
      db.createObjectStore(cacheName)
    }
  }
}

/**
 * indexDB 加载器（使用indexDB进行缓存）
 */
export abstract class IndexDbLoadHandler implements BaseLoadHandler {
  private modelName: string
  constructor(modelName: string) {
    this.modelName = modelName
  }
  abstract onStatusChange(callback: (status: LoadStatus) => void): void
  supportCacheLoad() {
    return this.existsCached()
  }
  abstract load(): Promise<any>
  abstract getLoadBytes(): number
  abstract getStatus(): LoadStatus
  abstract getLoadProgress(): number
  abstract onProgressChange(callback: (progress: number) => void): void

  async existsCached() {
    if (typeof window == 'undefined') {
      return false
    }
    let resolve: (value: boolean) => void
    const p = new Promise<boolean>((res) => (resolve = res))
    await awaitDbLoad
    // if (!db.objectStoreNames.contains(cacheName)) {
    //   db.createObjectStore(cacheName)
    //   resolve(false)
    //   return
    // }
    const transaction = db.transaction([cacheName], 'readonly')
    const objectStore = transaction.objectStore(cacheName)
    const request2 = objectStore.get(this.modelName)
    request2.onsuccess = () => {
      resolve(!!request2.result)
    }
    request2.onerror = () => {
      console.error('indexedDB get error')
      resolve(false)
    }
    return p
  }
  async setCache(data: Uint8Array) {
    await awaitDbLoad
    // if (!db.objectStoreNames.contains(cacheName)) {
    //   db.createObjectStore(cacheName)
    // }
    const transaction = db.transaction([cacheName], 'readwrite')
    const objectStore = transaction.objectStore(cacheName)
    const request2 = objectStore.put(data, this.modelName)
    request2.onsuccess = () => {
      console.log('indexedDB put success')
    }
    request2.onerror = () => {
      console.error('indexedDB put error')
    }
  }
  async getCache(): Promise<Uint8Array | null> {
    await awaitDbLoad
    let resolve: (value: Uint8Array) => void
    const p = new Promise<Uint8Array | null>((res) => (resolve = res))
    // if (!db.objectStoreNames.contains(cacheName)) {
    //   db.createObjectStore(cacheName)
    //   resolve(null)
    //   return
    // }
    const transaction = db.transaction([cacheName], 'readonly')
    const objectStore = transaction.objectStore(cacheName)
    const request2 = objectStore.get(this.modelName)
    request2.onsuccess = () => {
      resolve(request2.result)
    }
    request2.onerror = () => {
      console.error('indexedDB get error')
      resolve(null)
    }
    return p
  }
}
